package com.hframe.Json;

import com.google.gson.*;
import com.hframe.data.Base;
import com.hframe.data.DRef;
import com.hframe.type.*;
import com.hframe.typefilter.DateToString;
import com.hframe.typefilter.FilterRef;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.util.ArrayList;


/**
 * 对像转换成Json
 */
public class DataToJson implements FilterRef<Object, Object> {
    private static Logger log = Logger.getLogger(DataToJson.class);

    @Override
    public JsonElement filter(Object object) {

        if (null == object || object instanceof JsonNull) {
            return null;

        } else if (object instanceof JsonElement) {
            return (JsonElement) object;

        } else if (object instanceof Boolean) {
            return new JsonPrimitive((Boolean) object);

        } else if (object instanceof Number) {
            return new JsonPrimitive((Number) object);

        } else if (object instanceof String) {
            return new JsonPrimitive((String) object);

        } else if (object instanceof Character) {
            return new JsonPrimitive((Character) object);

        } else if (object instanceof Enum) {
            return new JsonPrimitive(((Enum) object).ordinal());

        } else if (object instanceof java.util.Date) {
            return new JsonPrimitive(DateToString.format((java.util.Date) object, DateToString.DATE_TIME_LINE));

        } else if (object instanceof DRef<?>) {
            return convert((DRef<?>) object);

        } else if (object instanceof ArrayList<?>) {
            //  return convert((ArrayList<?>) object);
            return null;
        } else {
            return new JsonPrimitive((String) object.toString());
        }

    }

    public static JsonElement convert(DRef<?> data, String... key) {
        JsonObject json = new JsonObject();
        Class<?> cla = data.getClass();
        while (null != cla && cla != DRef.class) {
            for (Field field : cla.getDeclaredFields()) {
                field.setAccessible(true);
                try {
                    if (!isExtendsClass(field.getType(), T.class))
                        continue;

                    String fieldName = field.getName();
                    Object object = field.get(data);

                    if (object == null) {
                        if (isOut(fieldName, key))
                            json.add(fieldName, null);
                    } else {
                        T<?> typeObj = (T<?>) object;
                        JsonFiletr filetr = typeObj.getfilter(JsonFiletr.class);
                        if (0 < key.length) {
                            if (!isOut(fieldName, key))
                                continue;
                        } else {
                            if (null != filetr && (filetr.getReadWait() == JsonFiletr.ReadWait.OnlyWait || filetr.getReadWait() == JsonFiletr.ReadWait.NotReadWait))
                                continue;
                        }
                        String name = null;
                        Object val;
                        String[] subkey = null;
                        if (null != filetr) {
                            name = filetr.getName();
                            val = filetr.get();
                            subkey = filetr.getKeys();
                        } else
                            val = typeObj.get();

                        if (null == name)
                            name = fieldName;

                        jsonAdd(json, name, val, null == subkey ? new String[0] : subkey);

                    }
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                }
            }
            cla = cla.getSuperclass();
        }

        return json;
    }


    public static JsonElement convert(ArrayList<? extends Base> array, String... key) {
        JsonArray json = new JsonArray();
        for (Base item : array) {
            if (null == item || (item instanceof T && ((T) item).isEmpty())) {
                json.add(null);

            } else if (item instanceof TBoolean) {
                json.add(new JsonPrimitive(((TBoolean) item).get()));

            } else if (item instanceof T.Number) {
                json.add(new JsonPrimitive((Number) ((T.Number) item).get()));

            } else if (item instanceof TString) {
                json.add(new JsonPrimitive(((TString) item).get()));

            } else if (item instanceof TDate) {
                json.add(new JsonPrimitive(DateToString.format(((TDate) item).get(), DateToString.DATE_TIME_LINE)));

            } else if (item instanceof TChar) {
                json.add(new JsonPrimitive(((TChar) item).get()));

            } else if (item instanceof TEnum) {
                json.add(new JsonPrimitive(((Enum) ((T<?>) item).get()).ordinal()));

            } else if (item instanceof DRef<?>) {
                json.add(convert((DRef<?>) item, key));

            } else if (item instanceof TArray<?>) {
                json.add(convert(((TArray<?>) item).get(), key));

            } else {
                json.add(new JsonPrimitive(item.toString()));
            }
        }
        return json;
    }

    public static JsonElement convert(TArray<?> array, String... key) {
        return convert(array.get(), key);
    }


    private static boolean isOut(String name, String[] keys) {
        for (String key : keys) {
            if (key.toLowerCase().equals(name.toLowerCase()))
                return true;
        }
        return false;
    }

    protected static void jsonAdd(JsonObject json, String name, Object val, String... outKey) {
        if (null == val || val instanceof JsonNull) {
            json.add(name, null);

        } else if (val instanceof JsonElement) {
            json.add(name, (JsonElement) val);

        } else if (val instanceof Boolean) {
            json.addProperty(name, (Boolean) val);

        } else if (val instanceof Number) {
            json.addProperty(name, (Number) val);

        } else if (val instanceof java.util.Date) {
            json.addProperty(name, DateToString.format((java.util.Date) val, DateToString.DATE_TIME_LINE));

        } else if (val instanceof String) {
            json.addProperty(name, (String) val);

        } else if (val instanceof Character) {
            json.addProperty(name, (Character) val);

        } else if (val instanceof Enum) {
            json.addProperty(name, ((Enum) val).ordinal());

        } else if (val instanceof DRef<?>) {
            json.add(name, convert((DRef<?>) val, outKey));

        } else if (val instanceof ArrayList<?>) {
            json.add(name, convert((ArrayList<Base>) val, outKey));

        } else {
            json.addProperty(name, val.toString());
        }
    }

    protected static boolean isExtendsClass(Class<?> src, Class<?> dis) {
        while (src != null) {
            if (src == dis)
                return true;
            src = src.getSuperclass();
        }
        return false;
    }
}